﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TiposComuns.Objectos;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using TiposComuns.Excepcoes;

namespace PADIbook
{
    #region Delegates
    delegate void SaveProfile(Profile profile);
    delegate void EndSaveDelegate();

    delegate void PostDelegate(string msg,DateTime date);
    delegate void EndPostDelegate();
    delegate List<Post> RefreshDelegate();
    delegate void EndRefreshDelegate(List<Post> list);

    delegate void RejectFriendDelegate(Person target);
    delegate void AcceptFriendDelegate(Person target);
    delegate void EndAcceptFriendDelegate(bool accept);

    delegate void RemoveFriendDelegate(Person target);
    delegate void EndRemoveFriendDelegate();

    delegate void StartUsernameSearchDelegate(string username);
    delegate void StartInterestSearchDelegate(string username);
    delegate void StartSexAgeSearchDelegate(int ageMin,int ageMax, bool sex);
    delegate void EndStartSearchDelegate();
    delegate void FillSearchResultsDelegate(List<Person> list);
    
    delegate void ErrorDelegate(Exception e);
    
    delegate void ChangeServerDelegate(int server);
    delegate void EndChangeServerDelegate();
    #endregion

    public partial class ProfileForm : Form
    {
        #region Attributes
        private Profile profile;
        private Cliente cliente;
        private Dictionary<string, Person> friends;     //lista dos amigos
        private Dictionary<string, Person> requests;    //lista dos pedidos de amizade recebidos
        private Dictionary<string, Person> sents;    //lista dos pedidos de amizade enviados
        private SortedList<Post, Post> posts = new SortedList<Post,Post>();
        RadioButton[] rbs = new RadioButton[3];
        private Dictionary<string, Person> searchedFriends;    //lista da info das pessoas procuradas
        #endregion

        #region Constructors
        public ProfileForm()
        {
            InitializeComponent();
            this.friends = new Dictionary<string, Person>();
            this.requests = new Dictionary<string, Person>();
            this.sents = new Dictionary<string, Person>();
            this.searchedFriends = new Dictionary<string, Person>();
            this.profile = Cliente.GetInstance().GetProfile();
            this.cliente = Cliente.GetInstance();
            FillProfile();
            FillWall(true);
            FillFriends();
            rbs[0] = rbc_serv1;
            rbs[1] = rbc_serv2;
            rbs[2] = rbc_serv3;
        }
        #endregion

        #region Init
        private void ProfileForm_Load(object sender, EventArgs e)
        {
            
        }

        private void FillFriends()
        {
            ListViewItem item;
            list_friends.Clear();
            list_received.Clear();
            list_sent.Clear();
            friends.Clear();
            requests.Clear();
            sents.Clear();
            foreach (Person person in cliente.GetFriends())
            {
                item = new ListViewItem();
                item.Text = person.Username;
                item.ImageIndex = 0;
                list_friends.Items.Add(item);

                friends.Add(person.Username, person);
            }

            foreach (Person person in cliente.GetFriendsReceived())
            {
                item = new ListViewItem();
                item.Text = person.Username;
                item.ImageIndex = 2;
                list_received.Items.Add(item);

                requests.Add(person.Username, person);
            }

            foreach (Person person in cliente.GetFriendsSent())
            {
                item = new ListViewItem();
                item.Text = person.Username;
                item.ImageIndex = 1;
                list_sent.Items.Add(item);

                sents.Add(person.Username, person);
            }

        }

        private void FillProfile()
        {
            l_username.Text = profile.Username;
            rb_fem.Checked = !profile.Genero;
            rb_masc.Checked = profile.Genero;
            lb_idade.Value = profile.Idade;
            ProcessInterests();
        }

        private void FillWall(bool flag)
        {
            if (flag)
            {
                this.posts.Clear();
                foreach (Post p in Cliente.GetInstance().GetPosts())
                {
                    this.posts.Add(p, p);
                }
            }
            wall.Items.Clear();
            ListViewItem item;
            foreach (Post post in this.posts.Values)
            {
                String s = String.Format("{2} disse {0}: {1}",post.Date,post.Message, post.User.Username);
                item = new ListViewItem((post.Date+" | "+post.User.Username+":"));
                //item.Text = s;
                item.ImageIndex = 3;
                item.SubItems.Add(post.Message);
                wall.Items.Add(item);     
            }
        }

        private void ProcessInterests()
        {
            if (profile.Interesses.Count == 0)
            {
                b_rem_inter.Enabled = false;
            }
            else
            {
                foreach (string inter in profile.Interesses)
                {
                    list_inter.Items.Remove(inter);
                    list_esc.Items.Add(inter);
                }
                if (profile.Interesses.Count == 5)
                    b_add_inter.Enabled = false;
            }
        }
        #endregion

        #region Callbacks
        private void EndRefresh(List<Post> list)
        {
            foreach (Post p in list)
            {
                this.posts.Add(p, p);
            }

            FillWall(false);
            b_refresh.Enabled = true;
        }

        private void Error(Exception e)
        {
            this.Invoke(new ErrorDelegate(ErrorCallback),e); 
        }

        private void ErrorCallback(Exception e)
        {
            MessageBox.Show("Não foi possível efectuar a acção", "Error");
        }

        private void CallbackRefresh(IAsyncResult res)
        {
            RefreshDelegate del = ((AsyncResult)res).AsyncDelegate as RefreshDelegate;

            try
            {
                List<Post> list = del.EndInvoke(res);
                this.Invoke(new EndRefreshDelegate(EndRefresh),list);
            }
            catch (Exception e) 
            {
                Error(e);
            }
        }

        private void EndSave()
        {
            b_save.Enabled = true;
            if (cliente.StateOK())
            {
                MessageBox.Show("O seu perfil foi salvo!");
            }
            else
            {
                MessageBox.Show("Não foi possível guardar o seu perfil!");
            }
        }

        private void CallBackSave(IAsyncResult res)
        {
            SaveProfile sp = ((AsyncResult)res).AsyncDelegate as SaveProfile;
            try
            {
                sp.EndInvoke(res);
                this.Invoke(new EndSaveDelegate(EndSave));
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        private void EndPost()
        {
            tb_post.Text = "";
            b_post.Enabled = true;

            if (!cliente.StateOK())
            {
                MessageBox.Show("Não foi possível efectuar o seu post!");
            }
            this.FillWall(true);
        }

        private void CallBackPost(IAsyncResult res)
        {
            try
            {
                PostDelegate pd = ((AsyncResult)res).AsyncDelegate as PostDelegate;
                pd.EndInvoke(res);

                this.Invoke(new EndPostDelegate(EndPost));
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        private void EndAcceptFriend(bool accept)
        {
            bt_accept.Enabled = true;
            Cursor = Cursors.Default;
            panel_request.Visible = false;
            selected = "";
            this.FillFriends();
            this.FillWall(true);
            if (cliente.StateOK())
            {
                if (accept)
                    MessageBox.Show("Aceitou o pedido de amizade.", "Confirmação", MessageBoxButtons.OK, MessageBoxIcon.Information);
                else
                    MessageBox.Show("Rejeitou o pedido de amizade.", "Confirmação", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                MessageBox.Show("Não foi possível efectuar a acção!");
            }
        }

        private void CallBackAcceptFriend(IAsyncResult res)
        {
            try
            {
                AcceptFriendDelegate ad = ((AsyncResult)res).AsyncDelegate as AcceptFriendDelegate;
                ad.EndInvoke(res);

                this.Invoke(new EndAcceptFriendDelegate(EndAcceptFriend), true);
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        private void CallBackRejectFriend(IAsyncResult res)
        {
            try
            {
                RejectFriendDelegate ad = ((AsyncResult)res).AsyncDelegate as RejectFriendDelegate;
                ad.EndInvoke(res);

                this.Invoke(new EndAcceptFriendDelegate(EndAcceptFriend), false);
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        public void EndRemoveFriend()
        {
            panel_details.Visible = false;
            b_remove.Enabled = false;
            FillFriends();

            if (cliente.StateOK())
            {
                MessageBox.Show("O amigo foi removido da lista.", "Confirmação", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                MessageBox.Show("Não foi possível efectuar a acção!");
            }
        }

        private void CallBackRemoveFriend(IAsyncResult res)
        {
            try
            {
                RemoveFriendDelegate ad = ((AsyncResult)res).AsyncDelegate as RemoveFriendDelegate;
                ad.EndInvoke(res);

                this.Invoke(new EndRemoveFriendDelegate(EndRemoveFriend));
            }
            catch (Exception e)
            {
                Error(e);
            }
        }
        #endregion

        #region Events
        private void b_save_Click(object sender, EventArgs e)
        {
            if (this.ValidateChildren())
            {
                b_save.Enabled = false;
                //profile.Username = tb_username.Text;
                profile.Idade = (int)lb_idade.Value;
                profile.Genero = rb_masc.Checked;
                
                AsyncCallback ac = new AsyncCallback(CallBackSave);
                SaveProfile sp = new SaveProfile(Cliente.GetInstance().SetProfile);
                sp.BeginInvoke(profile,ac,null);
            }
        }

        private void rb_masc_Click(object sender, EventArgs e)
        {
            rb_masc.Checked = true;
            rb_fem.Checked = false;
        }

        private void rb_fem_Click(object sender, EventArgs e)
        {
            rb_fem.Checked = true;
            rb_masc.Checked = false;
        }

        private void list_inter_SelectedIndexChanged(object sender, EventArgs e)
        {

            b_add_inter.Enabled = profile.Interesses.Count < 5;
            
            b_rem_inter.Enabled = false;
            int i = list_inter.SelectedIndex;
            list_esc.ClearSelected();
            list_inter.SelectedIndex = i;
        }

        private void list_esc_SelectedIndexChanged(object sender, EventArgs e)
        {
            b_add_inter.Enabled = false;
            if (profile.Interesses.Count > 0)
            {
                b_rem_inter.Enabled = true;
            }
            int i = list_esc.SelectedIndex;
            list_inter.ClearSelected();
            list_esc.SelectedIndex = i;
        }

        private void b_add_inter_Click(object sender, EventArgs e)
        {
            string inter = list_inter.SelectedItem as string;
            list_inter.Items.Remove(inter);
            list_esc.Items.Add(inter);
            profile.Interesses.Add(inter);
            b_add_inter.Enabled = false;
        }

        private void b_rem_inter_Click(object sender, EventArgs e)
        {
            if (list_esc.SelectedIndex >= 0 && list_esc.SelectedItem != null)
            {
                string inter = list_esc.SelectedItem as string;
                list_esc.Items.Remove(inter);
                list_inter.Items.Add(inter);
                profile.Interesses.Remove(inter);
                b_rem_inter.Enabled = false;
            }
        }

        

        private void b_post_Click(object sender, EventArgs e)
        {
            if (tb_post.Text.Length > 0)
            {
                b_post.Enabled = false;
                AsyncCallback ac = new AsyncCallback(CallBackPost);

                PostDelegate pd = new PostDelegate(Cliente.GetInstance().Post);
                pd.BeginInvoke(tb_post.Text,DateTime.Now,ac,null);
            }
        }

        private void b_add_Click(object sender, EventArgs e)
        {
            new AddFriend(friends,this.profile,cliente.GetAddress()).ShowDialog(this);
        }
        

        private void PadiBook_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Owner.Show();
        }

        private void bt_logoff_Click(object sender, EventArgs e)
        {
            Form1 f = new Form1();
            f.Show();
            this.Dispose();
        }

        private void b_refresh_Click(object sender, EventArgs e)
        {
            b_refresh.Enabled = false;

            AsyncCallback ac = new AsyncCallback(CallbackRefresh);
            RefreshDelegate del = new RefreshDelegate(this.cliente.RefreshWall);
            del.BeginInvoke(ac, null);
        }

        private void list_friends_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (list_friends.SelectedIndices.Count > 0)
            {
                Person p = friends[list_friends.FocusedItem.Text];
                lb_username.Text = p.Username;
                lb_endereco.Text = p.Address.URI + ":" + p.Address.Port;
                panel_details.Visible = true;
                b_remove.Enabled = true;
            }
            else
            {
                panel_details.Visible = false;
                b_remove.Enabled = false;
            }
        }

        private void list_friends_Leave(object sender, EventArgs e)
        {
            panel_details.Visible = false;
        }

        private string selected; //usado para saber qual foi o pedido de amizade recebido seleccionado
        private void list_received_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (list_received.SelectedIndices.Count > 0)
            {
                panel_request.Visible = true;
                selected = list_received.FocusedItem.Text;
            }
            else panel_request.Visible = false;
        }

        private void list_received_Leave(object sender, EventArgs e)
        {
            //panel_request.Visible = false;
        }

        private void bt_accept_Click(object sender, EventArgs e)
        {
            Person p = requests[selected];
            bt_accept.Enabled = false;
            Cursor = Cursors.WaitCursor;
            AsyncCallback ac = new AsyncCallback(CallBackAcceptFriend);
            AcceptFriendDelegate af = new AcceptFriendDelegate(cliente.AcceptFriend);
            af.BeginInvoke(p, ac, null);
            
        }

        private void bt_reject_Click(object sender, EventArgs e)
        {
            Person p = requests[selected];
            bt_reject.Enabled = false;
            Cursor = Cursors.WaitCursor;
            AsyncCallback ac = new AsyncCallback(CallBackRejectFriend);
            RejectFriendDelegate rf = new RejectFriendDelegate(cliente.RejectFriend);
            rf.BeginInvoke(p, ac, null);
        }

        private void b_remove_Click(object sender, EventArgs e)
        {
            Person p = friends[list_friends.FocusedItem.Text];

            AsyncCallback ac = new AsyncCallback(CallBackRemoveFriend);
            RemoveFriendDelegate rf = new RemoveFriendDelegate(cliente.RemoveFriend);
            rf.BeginInvoke(p, ac, null);
        }

        private void Group_Selected(object sender, TabControlEventArgs e)
        {
            if (Group.SelectedTab == Group.TabPages[2])
            {
                FillFriends();
            }
            if (Group.SelectedTab == Group.TabPages[1])
            {
                FillWall(true);
            }
        }
        #endregion

        #region Search
        private void tab_pesquisa_Click(object sender, EventArgs e)
        {
            
        }

        private void FillSearchResults(List<Person> list)
        {
            foreach (Person person in list)
            {
                list_results.Items.Add(person.Username);
                if (!searchedFriends.ContainsKey(person.Username))
                    searchedFriends.Add(person.Username, person);
            }
        }

        private void EndSearch()
        {
            b_search.Enabled = true;
        }

        private void EndSearchError()
        {
            b_search.Enabled = true;
        }

        private void CallBackEndStart(IAsyncResult res)
        {
            KeyValuePair<List<Person>, bool> pair;
            try
            {
                do
                {
                    pair = cliente.GetResults();

                    this.Invoke(new FillSearchResultsDelegate(FillSearchResults), pair.Key);
                    Thread.Sleep(100);
                } while (pair.Value);

                this.Invoke(new EndStartSearchDelegate(EndSearch));
            }
            catch (Exception)
            {
                MessageBox.Show("Não foi possível efectuar a pesquisa.");
                try
                {
                    this.Invoke(new EndStartSearchDelegate(EndSearch));
                }
                catch (Exception)
                {
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            switch(tab_filtros.SelectedIndex)
            {
                case 0:
                    {
                        string user = tb_suser.Text;
                        if (user.Length == 0)
                        {
                            MessageBox.Show("Tem de inserir um username");
                            return;
                        }
                        
                        AsyncCallback ac = new AsyncCallback(CallBackEndStart);
                        StartUsernameSearchDelegate del = new StartUsernameSearchDelegate(Cliente.GetInstance().StartLookupUsername);
                        del.BeginInvoke(user, ac, null);
                        break;
                    }
                case 1:
                    {
                        string interest = tb_sinterest.Text;
                        if (interest.Length == 0)
                        {
                            MessageBox.Show("Tem de escolher um interesse");
                            return;
                        }

                        AsyncCallback ac = new AsyncCallback(CallBackEndStart);
                        StartInterestSearchDelegate del = new StartInterestSearchDelegate(Cliente.GetInstance().StartLookupInterest);
                        del.BeginInvoke(interest, ac, null);
                        break;
                    }
                case 2:
                    {
                        int ageMin = (int)tb_sagemin.Value;
                        int ageMax = (int)tb_sagemax.Value;
                        if (ageMax < ageMin)
                        {
                            MessageBox.Show("A idade máxima não pode ser menor do que a mínima");
                            return;
                        }

                        bool sexo = rb_smasc.Checked;
                        AsyncCallback ac = new AsyncCallback(CallBackEndStart);
                        StartSexAgeSearchDelegate del = new StartSexAgeSearchDelegate(Cliente.GetInstance().StartLookupSexAge);
                        del.BeginInvoke(ageMin,ageMax,sexo, ac, null);

                        break;
                    }
            }
            b_search.Enabled = false;
            list_results.Items.Clear();
        }
        #endregion

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (list_sinterest.SelectedIndex >= 0)
            {
                tb_sinterest.Text = list_sinterest.SelectedItem as string;
            }
        }

        private void tabPage1_Click(object sender, EventArgs e)
        {
            this.SetServers();
        }

        private void SetServers()
        {
            State st = cliente.State;
            if (st.View.ViewState.Count > 0)
            {
                for (int i = 0; i < 3; i++)
                {
                    rbs[i].Checked = (i == st.View.ViewState[0]);
                }
            }
        }

        private void CallBackChangeServer(IAsyncResult res)
        {
            ChangeServerDelegate del = ((AsyncResult)res).AsyncDelegate as ChangeServerDelegate;
            try
            {
                del.EndInvoke(res);
                this.Invoke(new EndChangeServerDelegate(EndChangeServer));
                
            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        private void EndChangeServer()
        {
            this.SetServers();
            MessageBox.Show("Servidor alterado");
        }

        private void b_change_Click(object sender, EventArgs e)
        {
            int i; 
            bool b = false;
            for (i = 0; i < 3 && !b; i++)
            {
                b = rbs[i].Checked;
                if (b) break;
            }
            if (b)
            {
                AsyncCallback ac = new AsyncCallback(CallBackChangeServer);
                ChangeServerDelegate change = new ChangeServerDelegate(cliente.ChangeServer);
                change.BeginInvoke(i, ac, null);
            }
        }

        private void Group_SelectedIndexChanged(object sender, EventArgs e)
        {
            State st = cliente.State;
            if (st.View.ViewState.Count == 0)
            {
                //cliente faz logoff
                MessageBox.Show("Não tem servidores para efectuar operações. Faça logoff.");
                //this.Close();
            }
        }

        #region Adicionar amigo pesquisado
        private void list_results_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (list_results.SelectedIndices.Count > 0)
            {
                string username = list_results.SelectedItem.ToString();
                if (!friends.ContainsKey(username) && !sents.ContainsKey(username) && !requests.ContainsKey(username))
                {
                    bt_addfriendSearch.Visible = true;
                }
                
            }
            else
            {
                bt_addfriendSearch.Visible = false;
            }
        }

        private void bt_addfriendSearch_Click(object sender, EventArgs e)
        {
            Person p = searchedFriends[list_results.SelectedItem.ToString()];
            //MessageBox.Show("Adicionar: " + p.Username + p.Address.URI + p.Address.Port);
            
            AddFriend af = new AddFriend(friends, this.profile, cliente.GetAddress());
            
        }
#endregion

        private void list_results_Leave(object sender, EventArgs e)
        {
            bt_addfriendSearch.Visible = false;
        }
    }
}
